package net.qiqb.module.spring;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ClassUtil;
import lombok.extern.slf4j.Slf4j;
import net.qiqb.module.annotation.ModuleService;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

@Slf4j
public class ClassPathModuleServiceScanner extends ClassPathBeanDefinitionScanner {

    private Class<? extends ModuleApiFactoryBean> moduleApiFactoryBeanClass;

    private Class<? extends Annotation> annotationClass;

    static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";


    public ClassPathModuleServiceScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public Class<?> getModuleApiFactoryBeanClass() {
        return moduleApiFactoryBeanClass;
    }

    public void setModuleApiFactoryBeanClass(Class<? extends ModuleApiFactoryBean<?>> moduleApiFactoryBeanClass) {
        this.moduleApiFactoryBeanClass = moduleApiFactoryBeanClass == null ? ModuleApiFactoryBean.class : moduleApiFactoryBeanClass;
    }

    public void registerFilters() {
        boolean acceptAllInterfaces = true;

        // if specified, use the given annotation and / or marker interface
        if (this.annotationClass != null) {
            addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
            acceptAllInterfaces = false;
        }

        if (acceptAllInterfaces) {
            // default include filter that accepts all classes
            addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
        }

        // exclude package-info.java
        addExcludeFilter((metadataReader, metadataReaderFactory) -> {
            String className = metadataReader.getClassMetadata().getClassName();
            return className.endsWith("package-info");
        });
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {

        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

        if (beanDefinitions.isEmpty()) {
            log.warn("No Module api was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            processBeanDefinitions(beanDefinitions);
        }
        // 添加实现
        final BeanDefinitionRegistry registry = getRegistry();
        for (AnnotatedBeanDefinition annotatedBeanDefinition : moduleServiceBeanDefinitionHolder) {
            final AnnotationMetadata metadata = annotatedBeanDefinition.getMetadata();
            for (String interfaceName : metadata.getInterfaceNames()) {
                SpringModuleContext.getInstance(registry).setLocalImpl(interfaceName, annotatedBeanDefinition.getBeanClassName());
            }
        }
        return beanDefinitions;
    }

    private final Set<AnnotatedBeanDefinition> moduleServiceBeanDefinitionHolder = new HashSet<>();

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {

        AbstractBeanDefinition definition;
        final BeanDefinitionRegistry registry = getRegistry();
        for (BeanDefinitionHolder holder : beanDefinitions) {
            definition = (AbstractBeanDefinition) holder.getBeanDefinition();
            // 接口名称
            String beanClassName = definition.getBeanClassName();

            definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);

            definition.getPropertyValues().add("apiInterface", ClassUtil.loadClass(beanClassName));
            definition.getPropertyValues().add("moduleContext", SpringModuleContext.getInstance(registry));

            definition.setBeanClass(this.moduleApiFactoryBeanClass);

            definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClassName);

            SpringModuleContext.getInstance(registry).addModuleInterface(beanClassName);
        }


    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        final boolean isInterface = beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
        if (!isInterface) {
            final String beanClassName = beanDefinition.getBeanClassName();
            final Class<?> moduleServiceImplClass = ClassUtil.loadClass(beanClassName);
            ModuleService moduleService = AnnotationUtil.getAnnotation(moduleServiceImplClass, ModuleService.class);
            if (moduleService != null) {
                moduleServiceBeanDefinitionHolder.add(beanDefinition);
            }
            // 抛弃的
            final cn.qiqb.module.annotation.ModuleService annotation = AnnotationUtil.getAnnotation(moduleServiceImplClass, cn.qiqb.module.annotation.ModuleService.class);
            if (annotation != null) {
                moduleServiceBeanDefinitionHolder.add(beanDefinition);
            }
        }
        return isInterface;
    }

    public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
        this.annotationClass = annotationClass;
    }
}
